/*
 * AIEngine a new generation network intrusion detection system.
 *
 * Copyright (C) 2013-2023  Luis Campo Giralte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 * Written by Luis Campo Giralte <luis.camp0.2009@gmail.com> 
 *
 */
#include "ICMPProtocol.h"
#include <iomanip> // setw

namespace aiengine {

ICMPProtocol::ICMPProtocol():
	Protocol("ICMP") {}

bool ICMPProtocol::check(const Packet &packet) {

	int length = packet.getLength();

	if (length >= header_size) {
		setHeader(packet.getPayload());
		++total_valid_packets_;
		return true;
	} else {
		++total_invalid_packets_;
		return false;
	}
}

void ICMPProtocol::statistics(std::basic_ostream<char> &out, int level, int32_t limit) const {

	showStatisticsHeader(out, level);

	if (level > 3) {
		out << "\t" << "Total echo requests:    " << std::setw(10) << total_echo_request_ << "\n"
			<< "\t" << "Total echo replays:     " << std::setw(10) << total_echo_replay_ << "\n"
			<< "\t" << "Total dest unreachables:" << std::setw(10) << total_destination_unreachable_ << "\n"
			<< "\t" << "Total source quenchs:   " << std::setw(10) << total_source_quench_ << "\n"
			<< "\t" << "Total redirects:        " << std::setw(10) << total_redirect_ << "\n"
			<< "\t" << "Total rt advertistments:" << std::setw(10) << total_router_advertisment_ << "\n"
			<< "\t" << "Total rt solicitations: " << std::setw(10) << total_router_solicitation_ << "\n"
			<< "\t" << "Total ttl exceededs:    " << std::setw(10) << total_ttl_exceeded_ << "\n"
			<< "\t" << "Total timestamp reqs:   " << std::setw(10) << total_timestamp_request_ << "\n"
			<< "\t" << "Total timestamp reps:   " << std::setw(10) << total_timestamp_replay_ << "\n"
			<< "\t" << "Total info request:     " << std::setw(10) << total_info_request_ << "\n"
			<< "\t" << "Total info replays:     " << std::setw(10) << total_info_reply_ << "\n"
			<< "\t" << "Total address request:  " << std::setw(10) << total_address_ << "\n"
			<< "\t" << "Total address replays:  " << std::setw(10) << total_address_reply_ << "\n"
			<< "\t" << "Total others:           " << std::setw(10) << total_others_ << std::endl;
	}
	if ((level > 5)and(mux_.lock()))
		mux_.lock()->statistics(out);
}

void ICMPProtocol::statistics(Json &out, int level) const {

	showStatisticsHeader(out, level);

        if (level > 3) {
		out["echo_requests"] = total_echo_request_;
		out["echo_replays"] = total_echo_replay_;
		out["dest_unreachables"] = total_destination_unreachable_;
		out["source_quenchs"] = total_source_quench_;
		out["redirects"] = total_redirect_;
		out["route_advertisments"] = total_router_advertisment_;
		out["route_solicitations"] = total_router_solicitation_;
		out["ttl_exceededs"] = total_ttl_exceeded_;
		out["timestamp_requests"] = total_timestamp_request_;
		out["timestamp_replays"] = total_timestamp_replay_;
		out["info_requests"] = total_info_request_;
		out["info_replays"] = total_info_reply_;
		out["masked_address_request"] = total_address_;
		out["masked_address_replays"] = total_address_reply_;
		out["others"] = total_others_;
        }
}

bool ICMPProtocol::processPacket(Packet &packet) {

	CPUCycle cycles(&total_cpu_cycles_);
	uint16_t type = getType();

	if (type == ICMP_ECHO)
		++total_echo_request_;
	else if (type == ICMP_ECHOREPLY) 
		++total_echo_replay_;	
	else if (type == ICMP_UNREACH) 
		++total_destination_unreachable_;
	else if (type == ICMP_SOURCEQUENCH) 
		++total_source_quench_;
	else if (type == ICMP_REDIRECT) 
		++total_redirect_;
	else if (type == ICMP_ROUTERADVERT) 
		++total_router_advertisment_;
	else if (type == ICMP_ROUTERSOLICIT) 
		++total_router_solicitation_;
	else if (type == ICMP_TIMXCEED) 
		++total_ttl_exceeded_;
	else if (type == ICMP_TSTAMP) 
		++total_timestamp_request_;
	else if (type == ICMP_TSTAMPREPLY) 
		++total_timestamp_replay_;
	else if (type == ICMP_INFO_REQUEST)
		++total_info_request_;
	else if (type == ICMP_INFO_REPLY)
		++total_info_reply_;
	else if (type == ICMP_ADDRESS)
		++total_address_;
	else if (type == ICMP_ADDRESSREPLY)
		++total_address_reply_;
	else
		++total_others_;

	total_bytes_ += packet.getLength();
	++total_packets_;

	return true;	
}

CounterMap ICMPProtocol::getCounters() const {
	CounterMap cm;

	cm.addKeyValue("packets", total_packets_);
	cm.addKeyValue("bytes", total_bytes_);
	cm.addKeyValue("echo", total_echo_request_);
	cm.addKeyValue("echoreplay", total_echo_replay_);
        cm.addKeyValue("destination unreach", total_destination_unreachable_);
        cm.addKeyValue("source quench", total_source_quench_);
        cm.addKeyValue("redirect", total_redirect_);
        cm.addKeyValue("router advertisment", total_router_advertisment_);
        cm.addKeyValue("router solicitation", total_router_solicitation_);
        cm.addKeyValue("time exceeded", total_ttl_exceeded_);
	cm.addKeyValue("timestamp request", total_timestamp_request_);
	cm.addKeyValue("timestamp replay", total_timestamp_replay_);
	cm.addKeyValue("info request", total_info_request_);
	cm.addKeyValue("info replay", total_info_reply_);
	cm.addKeyValue("address request", total_address_);
	cm.addKeyValue("address replay", total_address_reply_);

        return cm;
}

void ICMPProtocol::resetCounters() {

	reset();

        total_echo_request_ = 0;
        total_echo_replay_ = 0;
        total_destination_unreachable_ = 0;
        total_source_quench_ = 0; // Router with congestion
        total_redirect_ = 0;
        total_router_advertisment_ = 0;
        total_router_solicitation_ = 0;
        total_ttl_exceeded_ = 0;
        total_timestamp_request_ = 0;
        total_timestamp_replay_ = 0;
        total_info_request_ = 0;
        total_info_reply_ = 0;
        total_address_ = 0;
        total_address_reply_ = 0;
        total_others_ = 0;
}

} // namespace aiengine
 
